/*******************************************************************************
 * Copyright 2010 Simon Mieth
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
/*
 * Created on 09.11.2008
 *
 */
package cn.kkdlk.parse.dxf.generator.conf;

import cn.kkdlk.parse.dxf.generator.DXFGenerationContext;
import cn.kkdlk.parse.dxf.generator.DXFGeneratorManagerImpl;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

public class SAXDXFGenerationContextBuilder extends org.xml.sax.helpers.DefaultHandler {

    public final static String NAMESPACE = "http://kabeja.org/config/profile/1.0";
    public final static String ELEMEN_PROFILES = "profiles";
    public final static String ELEMEN_PROFILE = "profile";
    public final static String ELEMENT_TYPE = "type";
    public final static String ELEMENT_SUBTYPE = "subtype";
    public final static String ELEMENT_TYPEHANDLER = "typehandler";
    public final static String ELEMENT_TYPEHANDLERS = "typehandlers";
    public final static String ATTRIBUTE_NAME = "name";
    public final static String ATTRIBUTE_CLASS = "class";
    public final static String ATTRIBUTE_ID = "id";
    public final static String ATTRIBUTE_REF_ID = "ref-id";
    public final static String ATTRIBUTE_ATTRIBUTE = "type";
    public final static String ATTRIBUTE_GROUPCODES = "groupCodes";
    public final static String ATTRIBUTE_DEFAULT = "default";
    private static final String CONFIG_HANDLERS = "blocks/dxf/conf/handlers.properties";
    protected DefaultDXFGenerationContext context;

    protected DXFType currentType;

    protected DXFProfile currentProfile;

    protected Map<String, Object> references = new HashMap<String, Object>();

    protected DXFGeneratorManagerImpl manager;


    public DXFGenerationContext buildDXFGenerationContext(InputStream in) {
        this.initialize();
        context = new DefaultDXFGenerationContext(this.manager);

        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setNamespaceAware(true);

            XMLReader saxparser = factory.newSAXParser().getXMLReader();

            saxparser.setContentHandler(this);
            saxparser.parse(new InputSource(in));
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        }

        return context;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
     * java.lang.String, java.lang.String, org.xml.sax.Attributes)
     */
    public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {

        try {
            if (uri.equals(NAMESPACE)) {
                if (localName.equals(ELEMENT_SUBTYPE)) {
                    if (isReferenced(attributes)) {
                        DXFSubType subType = (DXFSubType) getReferencedObject(attributes);
                        currentType.addDXFSubType(subType);
                    } else {
                        String[] codes = attributes.getValue(ATTRIBUTE_GROUPCODES).split(",");
                        int[] groupCodes = new int[codes.length];
                        for (int i = 0; i < codes.length; i++) {
                            groupCodes[i] = Integer.parseInt(codes[i]);
                        }
                        DXFSubType subType = new DXFSubType(attributes.getValue(ATTRIBUTE_NAME), groupCodes);
                        currentType.addDXFSubType(subType);
                        this.updateReference(attributes, subType);
                    }
                } else if (localName.equals(ELEMENT_TYPE)) {
                    currentType = new DXFType(attributes.getValue(ATTRIBUTE_NAME));
                    currentProfile.addDXFType(currentType);
                } else if (localName.equals(ELEMEN_PROFILE)) {
                    currentProfile = new DXFProfile(attributes.getValue(ATTRIBUTE_NAME));
                    this.manager.addDXFProfile(currentProfile);
                } else if (localName.equals(ELEMEN_PROFILES)) {
                    String defaultProfile = attributes.getValue(ATTRIBUTE_DEFAULT);
                    if (defaultProfile != null) {
                        this.context.addAttribute(DXFGenerationContext.ATTRIBUTE_DEFAULT_PROFILE, defaultProfile);
                    }
                } else if (localName.equals(ELEMENT_TYPEHANDLER)) {
                    String clazz = attributes.getValue(ATTRIBUTE_CLASS);
                    String type = attributes.getValue(ATTRIBUTE_NAME);
                    Object obj = this.getClass().getClassLoader().loadClass(clazz).newInstance();
                    this.manager.addHandler(type, obj);
                }
            }
        } catch (Exception e) {
            throw new SAXException("Could not create context for dxf generation.", e);
        }

    }

    protected int[] parseGroupCodes(String groupCodes) throws Exception {
        if (groupCodes.trim().length() > 0) {
            String[] elements = groupCodes.split(",");
            int[] codes = new int[elements.length];
            for (int i = 0; i < elements.length; i++) {
                codes[i] = Integer.parseInt(elements[i]);
            }

            return codes;
        } else {
            return new int[0];
        }
    }


    protected boolean isReferenced(Attributes atts) throws Exception {
        String id = atts.getValue(ATTRIBUTE_REF_ID);
        if (id != null) {
            if (this.references.containsKey(id)) {
                return true;
            } else {
                throw new SAXException("The reference:" + id + " must be declared before");
            }
        }
        return false;
    }

    protected void updateReference(Attributes atts, Object source) {
        String id = atts.getValue(ATTRIBUTE_ID);
        if (id != null) {
            this.references.put(id, source);
        }
    }

    protected Object getReferencedObject(Attributes atts) {
        String id = atts.getValue(ATTRIBUTE_REF_ID);

        if (id != null) {
            return this.references.get(id);
        }
        return null;
    }


    protected void initialize() {
        this.manager = new DXFGeneratorManagerImpl();
    }

}
